﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace LockCrypt.Core {
    public class LockCryptBLL {
        /// <summary>
        /// Arranges account groups into a heirarchy.
        /// </summary>
        /// <param name="groups">The groups.</param>
        /// <returns>All top level groups, with child groups and in a heirarchy</returns>
        public static IEnumerable<IGroup> ArrangeGroups(IEnumerable<IGroup> groups) {
            var lookup = groups.ToDictionary(g => g.ID ?? "-1");
            //var lookup = groups.Distinct(delegate() {  }).ToDictionary(g => g.ID ?? "-1");
            //var lookup = groups.ToLookup(g => g.ID).SelectMany(g => g).ToDictionary(g => g.ID ?? "-1", g => g);
            foreach(IGroup group in groups.Where(g => g.ParentID != null)) {
                IGroup parent = null;
                if(lookup.TryGetValue(group.ParentID, out parent)) {
                    parent.Groups.Add(group);
                }
            }
            IEnumerable<IGroup> topLevelGroups = groups.Where(g => g.ParentID == null);
            return topLevelGroups;
        }

        /// <summary>
        /// Arranges account groups into a heirarchy.
        /// </summary>
        /// <param name="groups">The groups.</param>
        /// <param name="accounts">The accounts, can be <c>null</c>.</param>
        /// <returns>All top level items, with child groups and accounts arranged in a heirarchy</returns>
        public static IEnumerable<INestable> ArrangeGroups(IEnumerable<IGroup> groups, IEnumerable<IAccount> accounts) {
            List<INestable> nestedGroups = ArrangeGroups(groups).Cast<INestable>().ToList();
            IEnumerable<INestable> rootAccounts = new IAccount[0];
            if(accounts != null) {
                var groupedAccounts = accounts.GroupBy(a => a.ParentID ?? "-1")
                    .ToDictionary(a => a.Key ?? "-1", a => a); // prevent null dictionary key
                foreach(IGroup group in groups) {
                    IGrouping<string, IAccount> accoutsInGroup = null;
                    if(groupedAccounts.TryGetValue(group.ID ?? "-1", out accoutsInGroup)) {
                        foreach(IAccount acc in accoutsInGroup) {
                            group.Accounts.Add(acc);
                        }
                    }
                }
                if(groupedAccounts.ContainsKey("-1")) {
                    rootAccounts = groupedAccounts["-1"].Cast<INestable>();
                }
            }
            nestedGroups.Cast<INestable>().ToList();
            nestedGroups.AddRange(rootAccounts);
            return nestedGroups;
        }

        /// <summary>
        /// Toggles a meta flag on a field.
        /// </summary>
        /// <param name="field">The field.</param>
        /// <param name="flag">The flag to toggle.</param>
        public static void ToggleMetaFlag(IField field, FieldMetaData flag) {
            bool isUrl = ((field.Meta & flag) == flag);
            if(isUrl) {
                field.Meta &= ~flag;
                Console.WriteLine(string.Format("{0} was {1}, flag unset", field.Name, flag.ToString()));
            } else {
                field.Meta |= flag;
                Console.WriteLine(string.Format("{0} not {1}, flag set", field.Name, flag.ToString()));
            }
        }

        /// <summary>
        /// Moves a field in relation to other fields.
        /// </summary>
        /// <param name="field">The field to move.</param>
        /// <param name="fields">The other fields.</param>
        /// <param name="direction">The direction. Greater than zero specifies down, less than zero specifies up.</param>
        public static void MoveField(IField field, IEnumerable<IField> fields, int direction) {
            int currentPos = field.SortIndex;
            if(direction > 0) { // move down
                IEnumerable<IField> otherFields = fields.Where(f => f.SortIndex == currentPos + 1);
                foreach(var other in otherFields) {
                    other.SortIndex--;
                }
                field.SortIndex++;
            } else if(direction < 0) { // move up (decrease sortindex)
                IEnumerable<IField> otherFields = fields.Where(f => f.SortIndex == currentPos - 1);
                foreach(var other in otherFields) {
                    other.SortIndex++;
                }
                field.SortIndex--;
            }
        }

        /// <summary>
        /// Loads the base names of all icons in the /Icons directory in the format &lt;baseName, fileName&gt;.
        /// </summary>
        /// <param name="requiredSize">The size by which to filter (including images with unspecified sizes).</param>
        /// <returns>A dictionary containing &lt;baseName, fileName&gt; of images whose names match the filename_size format, and all images with an unspecified size.</returns>
        public static Dictionary<string, string> GetIconNames(int requiredSize) {
            string iconDir = Path.Combine(Lime49.Utils.GetApplicationPath(), "Icons");
            string[] files = Directory.GetFiles(iconDir);
            //List<string> fileNames = new List<string>();
            Dictionary<string, string> iconNames = new Dictionary<string, string>(); // <basename, filename for requested size>
            foreach(string fileName in files) {
                string noExtension = Path.GetFileNameWithoutExtension(fileName);
                int lastUnderscore = noExtension.LastIndexOf('_');
                string baseName; // the root name of the image (without the '_size.extension' part)
                int namedSize; // the size according to the filename
                if(lastUnderscore > 0 && lastUnderscore + 1 < noExtension.Length) {
                    baseName = noExtension.Substring(0, lastUnderscore);
                    try {
                        namedSize = int.Parse(noExtension.Substring(lastUnderscore + 1));
                    }catch {
                        // size unparsable, assume it's the right size
                        namedSize = requiredSize;
                    }
                } else {
                    baseName = noExtension;
                    namedSize = requiredSize; // assume it's the required size
                }
                if(namedSize == requiredSize && !iconNames.ContainsKey(baseName)) {
                    iconNames.Add(baseName, fileName);
                }
            }
            return iconNames;
        }
    }
}
